<template>
  <div class="row wrap">
    <div v-if="readonly && fileArray.length === 0" class="rd-input-text">无</div>
    <div class="item-box" v-for="(item, index) in fileArray" :key="index" @click="imgTouch(item, index)">
      <img v-if="type === 'img'" :src="(item.path as string) || item.url" />

      <div v-if="type === 'video'" class="video-box">
        <video
          :src="(item.path as string) || item.url"
          x5-video-player-type="h5"
          x5-playsinline
          playsinline
          webkit-playsinline="true"
          muted
          preload="auto"
          @click="fullScreenPlay"
          @canplay="showVideoCovers"
        ></video>
      </div>

      <div class="loading" style="color: #ffffff" v-if="item.upStatus === 'selected'">
        <span>等待上传</span>
      </div>

      <div class="loading" v-if="item.upStatus === 'uploading'">
        <van-circle
          :current-rate="item.currentRate"
          :rate="100"
          :stroke-width="60"
          :text="item.currentRate + '%'"
          :color="gradientColor"
        />
      </div>

      <div class="fail" style="color: #fd482c" v-if="item.upStatus === 'fail'">
        {{ item.failReason }}
      </div>

      <span class="img-delete" v-if="item.upStatus !== 'uploading' && !disabled" @click.stop="imgDelete(index)">
        &#10005;</span
      >
    </div>

    <div class="item-box select-btn" v-if="fileArray.length < Number(max) && !disabled">
      <img class="select-img" src="../../assets/img/file-select.png" />
      <input
        class="file-ipt"
        ref="input"
        type="file"
        :accept="acceptType"
        contenteditable="false"
        @change="onFileSelected"
      />
    </div>
  </div>
</template>

<script lang="ts">
  import { defineComponent, reactive, ref, onMounted, computed, PropType, nextTick } from 'vue'
  import { ImagePreview, Toast, Circle } from 'vant'
  import { WeixinJSBridge } from '@/util'
  import { API } from '@/api'
  import * as qiniu from 'qiniu-js'
  type UP_STATUS = 'selected' | 'uploading' | 'success' | 'fail'

  type TYPE_FILE = 'img' | 'video'

  interface FileType {
    url?: string
    path?: string | ArrayBuffer
    upStatus: UP_STATUS
    type: TYPE_FILE
    fileName?: string
    file?: File
    failReason?: string
    qnId?: number
    currentRate: number
  }

  interface QiniuOpts {
    qiniuId: number
    qiniuKey: string
    qiniuToken: string
    resdomain: string
    qiniuZone: string
  }

  export default defineComponent({
    name: 'FileUploader',
    components: {
      [Circle.name]: Circle
    },
    props: {
      type: {
        type: String,
        default: () => 'img'
      },
      max: {
        type: [Number, String],
        default: () => 9
      },
      initValue: {
        type: Array as PropType<string[]>,
        default: () => []
      },
      disabled: {
        type: Boolean,
        default: () => false
      },
      readonly: {
        type: Boolean,
        default: () => false
      },
      mode: {
        type: String,
        default: () => 'auto'
      }
    },
    emits: ['change'],
    setup(props, ctx) {
      const fileArray = reactive<FileType[]>([])

      const input = ref<HTMLInputElement | null>(null)

      onMounted(() => {
        const arr: FileType[] = []
        ;(props.initValue as string[]).forEach((e) => {
          arr.push(
            reactive({
              fileName: e,
              url: e,
              upStatus: 'success',
              type: 'img',
              currentRate: 0
            })
          )
        })
        fileArray.push(...arr)

        nextTick(() => {
          triggerVideo()
        })
      })

      const imgDelete = (index: number) => {
        fileArray.splice(index, 1)
        if (props.mode === 'auto') {
          emitImg()
        }
      }

      // 弃用，命令模式用不到
      const emitImg = () => {
        const arr: string[] = []
        fileArray.forEach((file) => {
          if (file.upStatus === 'success') {
            arr.push(file.url!)
          }
        })
        ctx.emit('change', arr)
      }

      const clearInput = () => {
        if (input.value) {
          input.value.value = ''
        }
      }

      const onFileSelected = (e: any) => {
        const files = e.target.files
        // console.log('---------files------', files);
        if (files.length === 0) {
          return
        }

        const imgFile = files[0]
        if (props.type === 'img') {
          const reader = new FileReader()
          reader.onload = (e: any) => {
            // const dx = e.total / 1024 / 1024;
            // if (dx >= 10) {
            //   alert("img size cannot be larger than 10M");
            //   return;
            // }
            const newFile = reactive<FileType>({
              fileName: imgFile.name,
              file: imgFile,
              path: e.target.result,
              upStatus: 'selected',
              type: 'img',
              currentRate: 0
            })

            fileArray.push(newFile)
            clearInput()
            if (props.mode === 'auto') {
              upFileToCloudZone(newFile)
            }
          }
          reader.readAsDataURL(imgFile)
        } else {
          const videoUrl = URL.createObjectURL(imgFile)
          const newFile = reactive<FileType>({
            fileName: imgFile.name,
            file: imgFile,
            path: videoUrl,
            upStatus: 'selected',
            type: 'video',
            currentRate: 20
          })

          fileArray.push(newFile)
          clearInput()

          nextTick(() => {
            triggerVideo()
          })
          if (props.mode === 'auto') {
            upFileToCloudZone(newFile)
          }
        }
      }

      const triggerVideo = () => {
        if (WeixinJSBridge) {
          WeixinJSBridge.invoke('getNetworkType', {}, function () {
            showVideoCovers()
          })
        } else {
          showVideoCovers()
        }
      }

      const showVideoCovers = (e?: any) => {
        if (e) {
          e.target.play().then(() => {
            e.target.pause()
          })
        } else {
          Array.from(document.getElementsByTagName('video')).forEach((element) => {
            element.play().then(() => {
              element.pause()
            })
          })
        }
      }

      const getEmitArray = () => {
        return fileArray.map((f) => {
          return {
            success: f.upStatus === 'success',
            qnId: f.qnId,
            url: f.url
          }
        })
      }

      // 外部调用此方法开始上传
      const startUpload = (type: string) => {
        return new Promise<any[]>((resolve, reject) => {
          // @ts-ignore
          const waitUploadFiles: FileType[] = fileArray.filter((f) => {
            return f.upStatus === 'selected' || f.upStatus === 'fail'
          })

          const count = waitUploadFiles.length

          if (count === 0) {
            return resolve(getEmitArray())
          }

          const fileType = props.type

          const params = {
            type: type,
            videoCount: fileType === 'video' ? count : 0,
            imgCount: fileType === 'img' ? count : 0
          }

          API.getQiniuToken(params).then((res) => {
            if (res.success) {
              const tokens = fileType === 'img' ? res.data.imgs : res.data.videos

              const promiseArr = waitUploadFiles.map((f, index) => {
                return upFileToQiniu(f, tokens[index])
              })

              Promise.all(promiseArr).then(() => {
                resolve(getEmitArray())
              }, reject)
            } else {
              reject(new Error(res.msg))
            }
          })
        })
      }

      const upFileToQiniu = (fileObj: FileType, opts: QiniuOpts) => {
        return new Promise((resolve) => {
          fileObj.upStatus = 'uploading'
          fileObj.currentRate = 0

          const config = {
            useCdnDomain: true,
            region: qiniu.region.z0
          }

          const observable = qiniu.upload(fileObj.file, opts.qiniuKey, opts.qiniuToken, {}, config)

          const observer = {
            next(res: any) {
              fileObj.currentRate = parseInt(res.total.percent)
            },
            error(err: any) {
              fileObj.upStatus = 'fail'
              fileObj.failReason = err.message
              console.log('--error--', err)
              resolve({
                success: false,
                qnId: opts.qiniuId,
                url: ''
              })
            },
            complete(res: any) {
              console.log('--complete--', res)

              fileObj.upStatus = 'success'
              fileObj.url = `https://${opts.resdomain}/${opts.qiniuKey}`
              fileObj.qnId = opts.qiniuId

              resolve({
                success: true,
                qnId: opts.qiniuId,
                url: fileObj.url
              })
              // emitImg();
            }
          }

          observable.subscribe(observer) // 上传开始
        })
      }

      const upFileToCloudZone = (upFile: FileType) => {
        API.getQiniuToken({
          type: 1
        }).then((res) => {
          if (res.success) {
            const opt = {
              qiniuId: res.data.qiniuId,
              qiniuKey: res.data.qiniuKey,
              qiniuToken: res.data.qiniuToken,
              resdomain: res.data.resdomain,
              qiniuZone: res.data.qiniuZone
            }
            upFileToQiniu(upFile, opt)
          } else {
            upFile.upStatus = 'fail'
            upFile.failReason = res.msg
          }
        })
      }

      const imgTouch = (img: FileType, index: number) => {
        if (img.upStatus === 'success' || img.upStatus === 'selected') {
          if (props.type === 'video') {
            return
          }

          const imgs: string[] = fileArray.map((i) => {
            return (i.path as string) || i.url
          }) as string[]

          ImagePreview({
            images: imgs,
            startPosition: index
          })
          return
        }

        if (img.upStatus === 'uploading') {
          Toast('正在上传中...')
          return
        }

        if (img.upStatus === 'fail') {
          Toast(img.failReason)
          return
        }
      }

      const acceptType = computed(() => {
        return props.type === 'img' ? 'image/*' : 'video/*'
      })

      const fullScreenPlay = (e: any) => {
        if (e.target.webkitRequestFullScreen) {
          e.target.webkitRequestFullScreen()
        }
        if (e.target.webkitEnterFullScreen) {
          e.target.webkitEnterFullScreen()
        }
      }

      const gradientColor = {
        '0%': '#3fecff',
        '100%': '#6149f6'
      }

      return {
        fileArray,
        imgDelete,
        imgTouch,
        onFileSelected,
        input,
        startUpload,
        acceptType,
        fullScreenPlay,
        gradientColor,
        showVideoCovers
      }
    }
  })
</script>

<style scoped>
  .item-box {
    position: relative;
    width: 70px;
    height: 70px;
    border: 1px solid #eee;
    margin-bottom: 8px;
    margin-right: 8px;
    border-radius: 4px;
  }

  .item-box img,
  .item-box video,
  .item-box .loading,
  .item-box .fail {
    width: 100%;
    height: 100%;
  }

  .img-delete {
    padding: 0;
    background: rgba(0, 0, 0, 0.6);
    width: 16px;
    height: 16px;
    border-radius: 50%;
    color: #ffffff;
    font-size: 10px;
    position: absolute;
    right: -5px;
    top: -5px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .loading,
  .fail {
    position: absolute;
    top: 0;
    left: 0;
    padding: 6px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    font-size: 10px;
    background: rgba(0, 0, 0, 0.6);
    overflow: hidden;
    pointer-events: none;
  }

  .loading :deep(.van-circle__text) {
    color: #ffffff;
  }

  .video-box {
    width: 100%;
    height: 100%;
    position: relative;
  }

  .video-box::before {
    content: ' ';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 60% 60%;
    opacity: 0.8;
    background-image: url(../../assets/img/video_play.png);
  }

  .file-ipt {
    width: 100%;
    height: 100%;
    opacity: 0;
  }

  .select-btn {
    background: #f7f7f7;
  }
  .select-img {
    position: absolute;
    pointer-events: none;
    width: 24px !important;
    height: 24px !important;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
</style>
